package avicit.bdp.dds.api.service;

import avicit.bdp.common.service.dto.BdpColumnDTO;
import avicit.bdp.common.service.dto.BdpTableDTO;
import avicit.bdp.common.utils.BdpLogUtil;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.api.dto.lineage.*;
import avicit.bdp.dds.dao.entity.LineageFieldDTO;
import avicit.bdp.dds.dao.entity.LineageTableDTO;
import avicit.bdp.dds.dao.entity.ProcessDefinition;
import avicit.bdp.dds.dao.entity.ProjectUser;
import avicit.bdp.dds.dispatch.model.ProcessData;
import avicit.bdp.dds.dispatch.model.TaskNode;
import avicit.bdp.dds.dispatch.task.sql.SqlParameters;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.properties.PlatformConstant;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2024-07-15 14:38
 * @类说明：血缘关系服务
 * @修改记录：
 */
@Service
@Transactional
public class LineageRelationService {

    private static final Logger logger = LoggerFactory.getLogger(LineageRelationService.class);
    private static final int STEP_X = 350;
    private static final int STEP_Y = 700;

    @Autowired
    private LineageTableService lineageTableService;
    @Autowired
    private LineageFieldService lineageFieldService;
    @Autowired
    private ProcessDefinitionService processDefinitionService;
    @Autowired
    private ProjectUserService projectUserService;
    @Autowired
    private ConvertColumnClient convertColumnClient;

    /**
     * 查询表相关血缘
     *
     * @param tableId
     * @return
     */
    public LineageDto getTableLineageRelation(String tableId) {
        LineageDto lineageDto = new LineageDto();
        BdpTableDTO tableDTO = lineageTableService.selectTableById(tableId);
        if (tableDTO == null) {
            return lineageDto;
        }
        List<LineageTableDTO> lineageTableList = lineageTableService.getAllLineageTable();
        if (CollectionUtils.isEmpty(lineageTableList)) {
            return lineageDto;
        }

        // 递归查询血缘表id集合
        List<String> tableIds = getLineageTableIds(lineageTableList, tableDTO.getDataSourceId(), tableDTO.getId(), tableDTO.getName());
        List<BdpColumnDTO> allColumnList = lineageTableService.selectAllColumnByTableIds(tableIds);

        // 递归查询表间血缘
        Map<String, LineageTableDto> map = new HashMap<>();
        List<LineageTableDto> nodes = new ArrayList<>();

        // 递归查询表间血缘
        getLineageTableList(allColumnList, map, nodes, lineageTableList, tableDTO.getDataSourceId(), tableDTO.getId(), tableDTO.getName(), 0, 0, 1);
        getLineageTableList(allColumnList, map, nodes, lineageTableList, tableDTO.getDataSourceId(), tableDTO.getId(), tableDTO.getName(), 0, 0, 2);

        List<LineageEdgeDto> edges = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(nodes)) {
            List<String> tableIdList = nodes.stream().map(LineageTableDto::getId).collect(Collectors.toList());
            // 添加字段间血缘关系
            List<LineageFieldDTO> allLineageFieldList = lineageFieldService.selectAllLineageFieldByTableIds(tableIdList);
            addEdge(edges, allLineageFieldList, allColumnList);
        }

        lineageDto.setNodes(nodes);
        lineageDto.setEdges(edges);
        String logTitle = "查看表：【" + tableDTO.getName() + "】的数据血缘";
        BdpLogUtil.log("元数据管理模块", logTitle, PlatformConstant.OpType.select);
        return lineageDto;
    }

    /**
     * 递归查询表间血缘
     * @param allColumnList
     * @param map
     * @param nodes
     * @param lineageTableList
     * @param dataSourceId
     * @param tableId
     * @param tableName
     * @param level
     * @param height
     * @param direct  1:向前搜索  2:向后搜索
     * @return
     */
    private int getLineageTableList(List<BdpColumnDTO> allColumnList, Map<String, LineageTableDto> map, List<LineageTableDto> nodes,
                                          List<LineageTableDTO> lineageTableList, String dataSourceId, String tableId,
                                          String tableName, int level, int height, int direct) {
        LineageTableDto lineageTableDto = buildLineageTable(allColumnList, map, nodes, dataSourceId, tableId, tableName, level, height);

        List<LineageTableDTO> lineageTableDtoList;
        if(direct == 1){
            lineageTableDtoList = getSourceLineageTableList(lineageTableList, dataSourceId, tableName);
        }else{
            lineageTableDtoList = getTargetLineageTableList(lineageTableList, dataSourceId, tableName);
        }

        if (CollectionUtils.isNotEmpty(lineageTableDtoList)) {
            int i = 0;
            int nextLevelMaxHeight = 0;
            String datasource_id;
            String table_id;
            String tableCode;
            int newLevel;
            for (LineageTableDTO lineageTableDTO : lineageTableDtoList) {
                if(direct == 1){
                    datasource_id = lineageTableDTO.getSourceDatasourceId();
                    table_id = lineageTableDTO.getSourceTableId();
                    tableCode = lineageTableDTO.getSourceTableCode();
                    newLevel = level - 1;
                }else{
                    datasource_id = lineageTableDTO.getTargetDatasourceId();
                    table_id = lineageTableDTO.getTargetTableId();
                    tableCode = lineageTableDTO.getTargetTableCode();
                    newLevel = level + 1;
                }

                if (!map.containsKey(datasource_id + "-" + table_id)) {
//                    int nextHeight = height + i * STEP_Y;
                    int nextHeight = height;
                    if(i != 0){
                        LineageTableDTO lastTable = lineageTableDtoList.get(i - 1);
                        LineageTableDto lineageTable;
                        if(direct == 1){
                            lineageTable = map.get(lastTable.getSourceDatasourceId() + "-" + lastTable.getSourceTableId());
                        }else{
                            lineageTable = map.get(lastTable.getTargetDatasourceId() + "-" + lastTable.getTargetTableId());
                        }
                        int a = lineageTable.getY() + lineageTable.getHeight();
                        int b = lineageTable.getNextLevelMaxHeight();
                        if(a > b){
                            nextHeight = a + 100;
                        }else{
                            nextHeight = b + 100;
                        }
                    }
                    height = getLineageTableList(allColumnList, map, nodes, lineageTableList, datasource_id, table_id, tableCode, newLevel, nextHeight, direct);
                    nextLevelMaxHeight = height + 40 * getColumnSize(allColumnList, table_id);
                    i++;
                }
            }
            lineageTableDto.setNextLevelMaxHeight(nextLevelMaxHeight);
        }

        return height;
    }

    private LineageTableDto buildLineageTable(List<BdpColumnDTO> allColumnList, Map<String, LineageTableDto> map, List<LineageTableDto> nodes,
                              String dataSourceId, String tableId, String tableName, int level, int height){
        // 表宽度200,高度640
        int x = level * STEP_X;
        Point point = new Point(x, height);
        LineageTableDto lineageTableDto = buildLineageTableDto(allColumnList, tableId, tableName, point, level);
        String key = dataSourceId + "-" + tableId;
        if (!map.containsKey(key)) {
            nodes.add(lineageTableDto);
            map.put(key, lineageTableDto);
            return lineageTableDto;
        }else{
            return map.get(key);
        }
    }

    private LineageTableDto buildLineageTableDto(List<BdpColumnDTO> allColumnList, String tableId, String tableName, Point point, int level) {
        LineageTableDto lineageTableDto = new LineageTableDto();
        lineageTableDto.setId(tableId);
        lineageTableDto.setName(tableName);
        lineageTableDto.setX(point.getX());
        lineageTableDto.setY(point.getY());
        lineageTableDto.setLevel(level);
        if (CollectionUtils.isNotEmpty(allColumnList)) {
            List<BdpColumnDTO> tempList = allColumnList.stream().filter(
                    column -> column.getTableId().equals(tableId)).collect(Collectors.toList());
            List<BdpColumnDTO> columnList = tempList.stream()
                    .sorted(Comparator.comparing(BdpColumnDTO::getOrderBy, Comparator.nullsLast(Integer::compareTo)))
                    .collect(Collectors.toList());

            List<LineageFieldDto> columns = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(columnList)) {
                for (BdpColumnDTO columnDTO : columnList) {
                    LineageFieldDto lineageField = new LineageFieldDto();
                    lineageField.setId(columnDTO.getId());
                    lineageField.setName(columnDTO.getName());
                    lineageField.setColumnType(columnDTO.getColumnType());
                    columns.add(lineageField);
                }
            }
            lineageTableDto.setColumns(columns);
        }
        lineageTableDto.setHeight(40 * lineageTableDto.getColumnSize());
        return lineageTableDto;
    }

    /**
     * 返回表字段数量
     * @param allColumnList
     * @param tableId
     * @return
     */
    private int getColumnSize(List<BdpColumnDTO> allColumnList, String tableId) {
        if (CollectionUtils.isNotEmpty(allColumnList)) {
            List<BdpColumnDTO> columnList = allColumnList.stream().filter(
                    column -> column.getTableId().equals(tableId)).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(columnList)) {
                return columnList.size();
            }
        }
        return 0;
    }

    /**
     * 递归查询血缘表id集合
     * @param lineageTableList
     * @param dataSourceId
     * @param tableId
     * @param tableName
     * @return
     */
    private List<String> getLineageTableIds(List<LineageTableDTO> lineageTableList, String dataSourceId, String tableId, String tableName) {
        Map<String, String> map = new HashMap<>();
        List<String> tableIdList = new ArrayList<>();
        getLineageTableIds(map, tableIdList, lineageTableList, dataSourceId, tableId, tableName, 0);
        return tableIdList;
    }

    /**
     * 递归查询血缘表id集合
     * @param map
     * @param tableIdList
     * @param lineageTableList
     * @param dataSourceId
     * @param tableId
     * @param tableName
     * @param direct
     */
    private void getLineageTableIds(Map<String, String> map, List<String> tableIdList, List<LineageTableDTO> lineageTableList,
                                 String dataSourceId, String tableId, String tableName, int direct) {
        tableIdList.add(tableId);
        String key = dataSourceId + "-" + tableId;
        map.put(key, key);

        // 递归查询当前表的来源表
        if(direct == 0 || direct == 1){
            List<LineageTableDTO> sourceLineageTableList = getSourceLineageTableList(lineageTableList, dataSourceId, tableName);
            if (CollectionUtils.isNotEmpty(sourceLineageTableList)) {
                for(LineageTableDTO lineageTable : sourceLineageTableList){
                    if(!map.containsKey(lineageTable.getSourceDatasourceId() + "-" + lineageTable.getSourceTableId())){
                        getLineageTableIds(map, tableIdList, lineageTableList, lineageTable.getSourceDatasourceId(), lineageTable.getSourceTableId(),
                                lineageTable.getSourceTableCode(), 1);
                    }
                }
            }
        }

        // 递归查询当前表的目标表
        if(direct == 0 || direct == 2){
            List<LineageTableDTO> targetLineageTableList = getTargetLineageTableList(lineageTableList, dataSourceId, tableName);
            if (CollectionUtils.isNotEmpty(targetLineageTableList)) {
                for(LineageTableDTO lineageTable : targetLineageTableList){
                    if(!map.containsKey(lineageTable.getTargetDatasourceId() + "-" + lineageTable.getTargetTableId())){
                        getLineageTableIds(map, tableIdList, lineageTableList, lineageTable.getTargetDatasourceId(), lineageTable.getTargetTableId(),
                                lineageTable.getTargetTableCode(), 2);
                    }
                }
            }
        }
    }

    /**
     * 过滤血缘来源表
     * @param lineageTableList
     * @param dataSourceId
     * @param tableName
     * @return
     */
    private List<LineageTableDTO> getSourceLineageTableList(List<LineageTableDTO> lineageTableList, String dataSourceId, String tableName) {
        List<LineageTableDTO> sourceLineageTableList = lineageTableList.stream().filter(
                lineageTable -> lineageTable.getTargetDatasourceId().equals(dataSourceId)
                        && lineageTable.getTargetTableCode().equalsIgnoreCase(tableName)).collect(Collectors.toList());
        return sourceLineageTableList;
    }

    /**
     * 过滤血缘影响表
     * @param lineageTableList
     * @param dataSourceId
     * @param tableName
     * @return
     */
    private List<LineageTableDTO> getTargetLineageTableList(List<LineageTableDTO> lineageTableList, String dataSourceId, String tableName) {
        List<LineageTableDTO> targetLineageTableList = lineageTableList.stream().filter(
                lineageTable -> lineageTable.getSourceDatasourceId().equals(dataSourceId)
                        && lineageTable.getSourceTableCode().equalsIgnoreCase(tableName)).collect(Collectors.toList());
        return targetLineageTableList;
    }

    /**
     * 添加字段间血缘关系
     *
     * @param edges
     * @param allLineageFieldList
     * @param allColumnList
     */
    private void addEdge(List<LineageEdgeDto> edges, List<LineageFieldDTO> allLineageFieldList, List<BdpColumnDTO> allColumnList) {
        if (CollectionUtils.isEmpty(allLineageFieldList)) {
            return;
        }
        if (CollectionUtils.isEmpty(allColumnList)) {
            return;
        }
        String sourceColumnId = null;
        String targetColumnId = null;
        Map<String, String> map = new HashMap<>();
        for (LineageFieldDTO lineageField : allLineageFieldList) {
            List<BdpColumnDTO> sourceColumnList = allColumnList.stream().filter(
                    column -> column.getTableId().equals(lineageField.getSourceTableId())
                            && column.getName().equalsIgnoreCase(lineageField.getSourceFieldCode())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(sourceColumnList)) {
                sourceColumnId = sourceColumnList.get(0).getId();
            }

            List<BdpColumnDTO> targetColumnList = allColumnList.stream().filter(
                    column -> column.getTableId().equals(lineageField.getTargetTableId())
                            && column.getName().equalsIgnoreCase(lineageField.getTargetFieldCode())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(targetColumnList)) {
                targetColumnId = targetColumnList.get(0).getId();
            }

            if (StringUtils.isNotEmpty(sourceColumnId) && StringUtils.isNotEmpty(targetColumnId)) {
                String key = lineageField.getSourceTableId() + "-" + sourceColumnId + "-" + lineageField.getTargetTableId() + "-" + targetColumnId;
                if (!map.containsKey(key)) {
                    LineageEdgeDto lineageEdgeDto = new LineageEdgeDto();
                    lineageEdgeDto.setLineageFieldId(lineageField.getId());
                    lineageEdgeDto.setSource(lineageField.getSourceTableId());
                    lineageEdgeDto.setSourceColumn(sourceColumnId);
                    lineageEdgeDto.setTarget(lineageField.getTargetTableId());
                    lineageEdgeDto.setTargetColumn(targetColumnId);
                    edges.add(lineageEdgeDto);
                    map.put(key, key);
                }
            }
        }
    }

    /**
     * 根据字段血缘反查任务信息
     *
     * @param lineageFieldId
     * @return
     */
    public List<ProcessDefinition> getProcessDefinitionList(String lineageFieldId) {
        List<ProcessDefinition> list = new ArrayList<>();
        LineageFieldDTO lineageField = lineageFieldService.selectByPrimaryKey(lineageFieldId);
        if (lineageField == null) {
            return list;
        }

        LineageFieldDTO query = new LineageFieldDTO();
        query.setSourceDatasourceId(lineageField.getSourceDatasourceId());
        query.setSourceTableCode(lineageField.getSourceTableCode());
        query.setSourceFieldCode(lineageField.getSourceFieldCode());
        query.setTargetDatasourceId(lineageField.getTargetDatasourceId());
        query.setTargetTableCode(lineageField.getTargetTableCode());
        query.setTargetFieldCode(lineageField.getTargetFieldCode());
        List<LineageFieldDTO> lineageFieldList = lineageFieldService.selectList(query);
        if (CollectionUtils.isNotEmpty(lineageFieldList)) {
            List<String> idList = lineageFieldList.stream().map(LineageFieldDTO::getProcessDefinitionId).collect(Collectors.toList());
            list = lineageFieldService.getProcessDefinitionList(idList);
            // 设置查看流程定义权限
            setPermission(list);
            valueConvert(list);
        }

        return list;
    }

    /**
     * 设置查看流程定义权限
     *
     * @param list
     */
    private void setPermission(List<ProcessDefinition> list) {
        String userId = ThreadContextHelper.getUserId();
        logger.info("userId = {}", userId);
        if (StringUtils.isBlank(userId)) {
            userId = "1";
        }

        if (CollectionUtils.isNotEmpty(list)) {
            for (ProcessDefinition p : list) {
                if (userId.equals(p.getCreatedBy())) {
                    p.setReleaseState(1);
                } else {
                    ProjectUser projectUser = new ProjectUser();
                    projectUser.setProjectId(p.getProjectId());
                    projectUser.setUserId(userId);
                    List<ProjectUser> userList = projectUserService.selectList(projectUser);
                    if (CollectionUtils.isNotEmpty(userList)) {
                        p.setReleaseState(1);
                    } else {
                        p.setReleaseState(0);
                    }
                }
            }
        }
    }

    /**
     * 组装用户名称
     * @param list
     */
    private void valueConvert(List<ProcessDefinition> list) {
        //循环组装请求数据
        Map<String, Set<String>> convertFormData = new HashMap<>(4);
        for (ProcessDefinition dto : list) {
            BusinessUtil.createConvertSet(convertFormData, SystemConstant.USER, dto.getCreatedBy());
        }
        if (convertFormData.size() > 0) {
            //获取请求结果
            Map<String, Map<String, String>> convertResultData = convertColumnClient.replace(convertFormData);
            //循环设置Alias或Name的值
            for (ProcessDefinition dto : list) {
                dto.setCreatedBy(BusinessUtil.convertFormat(convertResultData, SystemConstant.USER, dto.getCreatedBy()));
            }
        }
    }

    /**
     * 查询任务SQL
     *
     * @param processDefinitionId
     * @return
     */
    public List<NodeDto> getProcessDefinitionSQL(String processDefinitionId) {
        List<NodeDto> nodeList = new ArrayList<>();
        ProcessDefinition processDefinition = processDefinitionService.selectByPrimaryKey(processDefinitionId);
        if (processDefinition == null) {
            return nodeList;
        }
        String processDefinitionJson = processDefinition.getProcessDefinitionJson();
        ProcessData processData = JSONUtils.parseObject(processDefinitionJson, ProcessData.class);
        if (processData == null) {
            return nodeList;
        }

        List<TaskNode> taskNodes = processData.getTasks();
        if (CollectionUtils.isNotEmpty(taskNodes)) {
            for (TaskNode taskNode : taskNodes) {
                if (taskNode.getType().equals("SQL")) {
                    List<String> preSqlList = new ArrayList();
                    List<String> postSqlList = new ArrayList();
                    //处理SQL节点
                    String params = taskNode.getParams();
                    SqlParameters p = JSONUtils.parseObject(params, SqlParameters.class);
                    if (StringUtils.isBlank(p.getDatasource())) {
                        continue;
                    }
                    preSqlList.addAll(p.getPreStatements());
                    postSqlList.addAll(p.getPostStatements());
                    preSqlList.removeAll(Collections.singleton(null));
                    postSqlList.removeAll(Collections.singleton(null));

                    NodeDto node = new NodeDto();
                    node.setId(taskNode.getId());
                    node.setName(taskNode.getName());
                    node.setSql(p.getSql());
                    node.setPreSql(StringUtils.join(preSqlList, ","));
                    node.setPostSql(StringUtils.join(postSqlList, ","));
                    nodeList.add(node);
                }
            }
        }

        return nodeList;
    }

}
